package jsr292.java.lang.invoke;

import jsr292.weaver.Debug;
import jsr292.weaver.opt.OptimizerContext;
import jsr292.weaver.opt.OptimizerSnippets;
import jsr292.weaver.opt.OptimizerVarAllocator;


class MHGuarder extends MethodHandle {
    private final MethodHandle test;
    final MethodHandle target;
    final MethodHandle fallback;
    
    public MHGuarder(MethodHandle test, MethodHandle target, MethodHandle fallback) {
        super(target.type());
        this.test = test;
        this.target = target;
        this.fallback = fallback;
    }
    
    @Override
    public Object $invoke$() throws Throwable {
        return ((Boolean)test.$invoke$())?
                target.$invoke$():
                fallback.$invoke$();
    }
    
    @Override
    public Object $invoke$(Object o1) throws Throwable {
        return ((Boolean)test.$invoke$(o1))?
            target.$invoke$(o1):
            fallback.$invoke$(o1);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2) throws Throwable {
        return ((Boolean)test.$invoke$(o1, o2))?
                target.$invoke$(o1, o2):
                fallback.$invoke$(o1, o2);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3) throws Throwable {
        return ((Boolean)test.$invoke$(o1, o2, o3))?
                target.$invoke$(o1, o2, o3):
                fallback.$invoke$(o1, o2, o3);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4) throws Throwable {
        return ((Boolean)test.$invoke$(o1, o2, o3, o4))?
                target.$invoke$(o1, o2, o3, o4):
                fallback.$invoke$(o1, o2, o3, o4);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5) throws Throwable {
        return ((Boolean)test.$invoke$(o1, o2, o3, o4, o5))?
                target.$invoke$(o1, o2, o3, o4, o5):
                fallback.$invoke$(o1, o2, o3, o4, o5);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6) throws Throwable {
        return ((Boolean)test.$invoke$(o1, o2, o3, o4, o5, o6))?
                target.$invoke$(o1, o2, o3, o4, o5, o6):
                fallback.$invoke$(o1, o2, o3, o4, o5, o6);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7) throws Throwable {
        return ((Boolean)test.$invoke$(o1, o2, o3, o4, o5, o6, o7))?
                target.$invoke$(o1, o2, o3, o4, o5, o6, o7):
                fallback.$invoke$(o1, o2, o3, o4, o5, o6, o7);
    }
    
    @Override
    public Object $invoke$(Object... rest) throws Throwable {
        return ((Boolean)test.$invoke$(rest))?
                target.$invoke$(rest):
                fallback.$invoke$(rest);
    }
    
    @Override
    public boolean isWeavable(Class<?> classBeingRedefined) {
        return test.isWeavable(classBeingRedefined) &&
               target.isWeavable(classBeingRedefined) &&
               fallback.isWeavable(classBeingRedefined);
    }
    @Override
    public void weave(OptimizerContext context, OptimizerVarAllocator allocator) {
        if (Debug.DEBUG) {
            Debug.checkAllocator(allocator, this);
        }
        
        OptimizerSnippets.guard(context, allocator, test, target, fallback);
    }
}
